﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CMWebService.cs" company="company">
//   copyright information
// </copyright>
// <summary>
//   CMWebService. Web services to access content.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace Alogient.Cameleon.Sdk
{
    using System;
    using System.IO;
    using System.Web.Services;
    using System.Xml;
    using System.Xml.Serialization;
    using Alogient.Cameleon.Sdk.Items;

    /// <summary>
    /// CMWebService. Web services to access content.
    /// </summary>
    [WebService(Namespace = "http://www.alogient.com/cameleon")]
    public class CMWebService : System.Web.Services.WebService
    {
        #region Page

        /// <summary>
        /// Validate if a page exists.
        /// </summary>
        /// <param name="navID">
        /// The nav id.
        /// </param>
        /// <param name="cultureCode">
        /// The culture code.
        /// </param>
        /// <returns>
        /// Wheter the page exists or not.
        /// </returns>
        [WebMethod]
        public bool PageExist(int navID, string cultureCode)
        {
            try
            {
                return CMServices.PageExists(navID, cultureCode);
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// Gets a page.
        /// </summary>
        /// <param name="navID">
        /// The nav id.
        /// </param>
        /// <param name="cultureCode">
        /// The culture code.
        /// </param>
        /// <returns>
        /// The xml of the page.
        /// </returns>
        [WebMethod]
        public XmlDocument GetPage(int navID, string cultureCode)
        {
            return this.GetPageArbo(navID, cultureCode, 1);
        }

        /// <summary>
        /// Get a page and its subpages.
        /// </summary>
        /// <param name="navID">
        /// The nav id.
        /// </param>
        /// <param name="cultureCode">
        /// The culture code.
        /// </param>
        /// <param name="depth">
        /// The depth.
        /// </param>
        /// <returns>
        /// The xml of the page and subpages.
        /// </returns>
        [WebMethod]
        public XmlDocument GetPageArbo(int navID, string cultureCode, int depth)
        {
            try
            {
                var page = new CMPage(navID, cultureCode) { XmlDepth = depth };
                var xmlDocument = this.Serialize(page);

                return xmlDocument;
            }
            catch (Exception ex)
            {
                return this.ConvertExceptionToXml(ex);
            }
        }

        /// <summary>
        /// Get a page and its subpages, without the elements
        /// </summary>
        /// <param name="navID">
        /// The nav id.
        /// </param>
        /// <param name="cultureCode">
        /// The culture code.
        /// </param>
        /// <param name="depth">
        /// The depth.
        /// </param>
        /// <returns>
        /// The xml of the page and subpages.
        /// </returns>
        [WebMethod]
        public XmlDocument GetPageOnlyArbo(int navID, string cultureCode, int depth)
        {
            try
            {
                var xmlDocument = new XmlDocument();
                var xmlDeclaration = xmlDocument.CreateXmlDeclaration("1.0", "utf-8", null);
                xmlDocument.InsertBefore(xmlDeclaration, xmlDocument.DocumentElement);

                var page = new CMPage(navID, cultureCode);

                xmlDocument = this.SerializePagesOnly(page, depth, null, xmlDocument);

                return xmlDocument;
            }
            catch (Exception ex)
            {
                return this.ConvertExceptionToXml(ex);
            }
        }

        /// <summary>
        /// Gets a page and its subpages, with specific elements.
        /// </summary>
        /// <param name="navID">
        /// The nav id.
        /// </param>
        /// <param name="cultureCode">
        /// The culture code.
        /// </param>
        /// <param name="depth">
        /// The depth.
        /// </param>
        /// <param name="elements">
        /// The elements.
        /// </param>
        /// <returns>
        /// The xml of the page and subpages.
        /// </returns>
        [WebMethod]
        public XmlDocument GetPageOnlyArboElements(int navID, string cultureCode, int depth, string elements)
        {
            try
            {
                var xmlDocument = new XmlDocument();
                var xmlDeclaration = xmlDocument.CreateXmlDeclaration("1.0", "utf-8", null);
                xmlDocument.InsertBefore(xmlDeclaration, xmlDocument.DocumentElement);

                var page = new CMPage(navID, cultureCode);

                xmlDocument = this.SerializePagesOnlyElements(page, depth, elements, null, xmlDocument);

                return xmlDocument;
            }
            catch (Exception ex)
            {
                return this.ConvertExceptionToXml(ex);
            }
        }

        /// <summary>
        /// Gets subpages of a page.
        /// </summary>
        /// <param name="parentID">
        /// The parent id.
        /// </param>
        /// <param name="cultureCode">
        /// The culture code.
        /// </param>
        /// <returns>
        /// The xml of the subpages.
        /// </returns>
        [WebMethod]
        public XmlDocument ListPages(int parentID, string cultureCode)
        {
            return this.ListPagesArbo(parentID, cultureCode, 1);
        }

        /// <summary>
        /// Gets subpages of a page and their subpages.
        /// </summary>
        /// <param name="parentID">
        /// The parent id.
        /// </param>
        /// <param name="cultureCode">
        /// The culture code.
        /// </param>
        /// <param name="depth">
        /// The depth.
        /// </param>
        /// <returns>
        /// The xml of the subpages.
        /// </returns>
        [WebMethod]
        public XmlDocument ListPagesArbo(int parentID, string cultureCode, int depth)
        {
            try
            {
                var pages = new CMPage(parentID, cultureCode).Subpages;
                foreach (var page in pages)
                {
                    page.XmlDepth = depth - 1;
                }

                var xmlDoc = this.Serialize(pages);
                this.AddRootAttribute(ref xmlDoc, "ParentID", parentID);
                this.AddRootAttribute(ref xmlDoc, "CultureCode", cultureCode);
                
                return xmlDoc;
            }
            catch (Exception ex)
            {
                return this.ConvertExceptionToXml(ex);
            }
        }

        /// <summary>
        /// Gets the breadcrumb.
        /// </summary>
        /// <param name="navID">
        /// The nav id.
        /// </param>
        /// <param name="cultureCode">
        /// The culture code.
        /// </param>
        /// <returns>
        /// The xml of the breadcrumb.
        /// </returns>
        [WebMethod]
        public XmlDocument Breadcrumbs(int navID, string cultureCode)
        {
            try
            {
                var xmlDoc = new XmlDocument();
                var xmlDeclaration = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", null);
                xmlDoc.InsertBefore(xmlDeclaration, xmlDoc.DocumentElement);

                var pagesNode = xmlDoc.CreateElement("ArrayOfPage");
                pagesNode.SetAttribute("NavID", navID.ToString());
                pagesNode.SetAttribute("CultureCode", cultureCode);

                foreach (CMPage page in new CMPage(navID, cultureCode).Breadcrumbs)
                {
                    pagesNode.AppendChild(this.SerializePageOnly(page, xmlDoc));
                }

                xmlDoc.AppendChild(pagesNode);
                return xmlDoc;
            }
            catch (Exception ex)
            {
                return this.ConvertExceptionToXml(ex);
            }
        }

        #endregion

        #region Search

        /// <summary>
        /// Gets pages that uses a template.
        /// </summary>
        /// <param name="templateName">
        /// The template name.
        /// </param>
        /// <param name="cultureCode">
        /// The culture code.
        /// </param>
        /// <returns>
        /// The xml of the pages.
        /// </returns>
        [WebMethod]
        public XmlDocument SearchPagesByTemplate(string templateName, string cultureCode)
        {
            try
            {
                var pages = CMServices.GetPagesByTemplate(templateName, cultureCode);
                var xmlDoc = this.Serialize(pages);
                this.AddRootAttribute(ref xmlDoc, "CultureCode", cultureCode);
                this.AddRootAttribute(ref xmlDoc, "TotalCount", pages.Count);

                return xmlDoc;
            }
            catch (Exception ex)
            {
                return this.ConvertExceptionToXml(ex);
            }
        }

        #endregion

        #region Serialization

        /// <summary>
        /// Serilalize an object.
        /// </summary>
        /// <param name="obj">
        /// The object to serialize.
        /// </param>
        /// <returns>
        /// A xml serialization of the object.
        /// </returns>
        private XmlDocument Serialize(object obj)
        {
            var serializer = new XmlSerializer(obj.GetType());
            var strWriter = new StringWriter();
            serializer.Serialize(strWriter, obj);

            var xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(strWriter.ToString());
            return xmlDoc;
        }

        /// <summary>
        /// Serialize a CMPage.
        /// </summary>
        /// <param name="page">
        /// The page to serialize.
        /// </param>
        /// <param name="xmlDoc">
        /// The xml doc.
        /// </param>
        /// <returns>
        /// A xml serialization of the page.
        /// </returns>
        private XmlElement SerializePageOnly(CMPage page, XmlDocument xmlDoc)
        {
            var pageNode = xmlDoc.CreateElement("Page");

            pageNode.SetAttribute("NavID", page.NavID.ToString());
            pageNode.SetAttribute("ParentID", page.ParentID.ToString());
            pageNode.SetAttribute("CultureCode", page.CultureCode);
            pageNode.SetAttribute("PageTitle", page.PageTitle);
            pageNode.SetAttribute("PageUrl", page.PageUrl);
            pageNode.SetAttribute("PageNavEnd", page.PageNavEnd.ToString());
            pageNode.SetAttribute("CreationDate", page.CreationDate.ToString());
            pageNode.SetAttribute("LastModified", page.LastModified.ToString());
            pageNode.SetAttribute("Author", page.Author);
            pageNode.SetAttribute("HasChildren", page.HasChildren.ToString());
            pageNode.SetAttribute("TemplateID", page.TemplateID.ToString());
            pageNode.SetAttribute("TemplateName", page.TemplateName);
            pageNode.SetAttribute("TemplateUrl", page.TemplateUrl);

            return pageNode;
        }

        /// <summary>
        /// Serialize a CMPage and it's elements.
        /// </summary>
        /// <param name="page">
        /// The page to serialize.
        /// </param>
        /// <param name="elements">
        /// The elements, separated by a comma.
        /// </param>
        /// <param name="xmlDoc">
        /// The xml doc.
        /// </param>
        /// <returns>
        /// A xml serialization of the page.
        /// </returns>
        private XmlElement SerializePageOnlyElements(CMPage page, string elements, XmlDocument xmlDoc)
        {
            var pageNode = xmlDoc.CreateElement("Page");

            pageNode.SetAttribute("NavID", page.NavID.ToString());
            pageNode.SetAttribute("ParentID", page.ParentID.ToString());
            pageNode.SetAttribute("CultureCode", page.CultureCode);
            pageNode.SetAttribute("PageTitle", page.PageTitle);
            pageNode.SetAttribute("PageUrl", page.PageUrl);
            pageNode.SetAttribute("PageNavEnd", page.PageNavEnd.ToString());
            pageNode.SetAttribute("CreationDate", page.CreationDate.ToString());
            pageNode.SetAttribute("LastModified", page.LastModified.ToString());
            pageNode.SetAttribute("Author", page.Author);
            pageNode.SetAttribute("HasChildren", page.HasChildren.ToString());
            pageNode.SetAttribute("TemplateID", page.TemplateID.ToString());
            pageNode.SetAttribute("TemplateName", page.TemplateName);
            pageNode.SetAttribute("TemplateUrl", page.TemplateUrl);

            foreach (string elem in elements.Split(','))
            {
                if (page.Elements.Exists(elem))
                {
                    var elementNode = xmlDoc.CreateElement("Element");

                    var element = page.Elements[elem];
                    elementNode.SetAttribute("ID", element.ElementID.ToString());
                    elementNode.SetAttribute("Name", element.ElementName);
                    elementNode.SetAttribute("Value", element.Value);
                    elementNode.SetAttribute("OriginalMediaFile", element.OriginalMediaFile);
                    elementNode.SetAttribute("PresentationMediaFile", element.PresentationImageFile);
                    elementNode.SetAttribute("ThumbnailMediaFile", element.ThumbnailImageFile);

                    pageNode.AppendChild(elementNode);
                }
            }

            return pageNode;
        }

        /// <summary>
        /// Serialize a CMPage and it's subpages.
        /// </summary>
        /// <param name="page">
        /// The page to serialize.
        /// </param>
        /// <param name="depth">
        /// The depth of subpages.
        /// </param>
        /// <param name="parentNode">
        /// The parent node.
        /// </param>
        /// <param name="xmlDoc">
        /// The xml doc.
        /// </param>
        /// <returns>
        /// A zml serialization of the page.
        /// </returns>
        private XmlDocument SerializePagesOnly(CMPage page, int depth, XmlElement parentNode, XmlDocument xmlDoc)
        {
            var pageNode = this.SerializePageOnly(page, xmlDoc);
            if (parentNode != null)
            {
                parentNode.AppendChild(pageNode);
            }
            else
            {
                xmlDoc.AppendChild(pageNode);
            }

            if (depth >= 1)
            {
                foreach (var p in page.Subpages)
                {
                    this.SerializePagesOnly(p, depth - 1, pageNode, xmlDoc);
                }
            }

            return xmlDoc;
        }

        /// <summary>
        /// Serialize a CMPage and it's subpages with their elements.
        /// </summary>
        /// <param name="page">
        /// The page to serialize.
        /// </param>
        /// <param name="depth">
        /// The depth of the subpages.
        /// </param>
        /// <param name="elements">
        /// The elements.
        /// </param>
        /// <param name="parentNode">
        /// The parent node.
        /// </param>
        /// <param name="xmlDoc">
        /// The xml doc.
        /// </param>
        /// <returns>
        /// A xml serialization of the page.
        /// </returns>
        private XmlDocument SerializePagesOnlyElements(CMPage page, int depth, string elements, XmlElement parentNode, XmlDocument xmlDoc)
        {
            var pageNode = this.SerializePageOnlyElements(page, elements, xmlDoc);
            if (parentNode != null)
            {
                parentNode.AppendChild(pageNode);
            }
            else
            {
                xmlDoc.AppendChild(pageNode);
            }

            if (depth >= 1)
            {
                foreach (var p in page.Subpages)
                {
                    this.SerializePagesOnlyElements(p, depth - 1, elements, pageNode, xmlDoc);
                }
            }

            return xmlDoc;
        }

        /// <summary>
        /// Converts and exception to xml.
        /// </summary>
        /// <param name="ex">
        /// The exception.
        /// </param>
        /// <returns>
        /// A xml version of the exception.
        /// </returns>
        private XmlDocument ConvertExceptionToXml(Exception ex)
        {
            var xmlDoc = new XmlDocument();

            XmlNode node = xmlDoc.CreateElement("Error");
            xmlDoc.AppendChild(node);

            var innerException = new Exception(string.Empty, ex);
            while ((innerException = innerException.InnerException) != null)
            {
                XmlNode excNode = xmlDoc.CreateElement("Exception");

                var typeAttr = xmlDoc.CreateAttribute("type");
                typeAttr.InnerText = innerException.GetType().Name;
                if (excNode.Attributes != null)
                {
                    excNode.Attributes.Append(typeAttr);
                }

                XmlAttribute msgAttr = xmlDoc.CreateAttribute("message");
                msgAttr.InnerText = innerException.Message;
                if (excNode.Attributes != null)
                {
                    excNode.Attributes.Append(msgAttr);
                }

                XmlNode stackNode = xmlDoc.CreateElement("StackTrace");
                stackNode.InnerText = innerException.StackTrace;
                excNode.AppendChild(stackNode);

                node.AppendChild(excNode);
                node = excNode;
            }

            return xmlDoc;
        }

        /// <summary>
        /// Add a root attribute
        /// </summary>
        /// <param name="xmlDoc">
        /// The xml doc.
        /// </param>
        /// <param name="name">
        /// The name of the attribute.
        /// </param>
        /// <param name="value">
        /// The value.
        /// </param>
        private void AddRootAttribute(ref XmlDocument xmlDoc, string name, object value)
        {
            XmlAttribute xmlAttribute = xmlDoc.CreateAttribute(name);
            xmlAttribute.Value = value.ToString();
            if (xmlDoc.DocumentElement != null)
            {
                xmlDoc.DocumentElement.Attributes.Append(xmlAttribute);
            }
        }
        #endregion
    }
}
